OAIからOACに変更してオリジンのS3バケットをKMSで暗号化してみた
CX事業本部Delivery部のアベシです。
CloudFrontの Origin Access Control
(OAC)ではOrigin Access Identity
(OAI)で使用できなかったKMSを使ったオリジンのホスト先バケットの暗号化が可能になっています。
OAIではSSE-S3での暗号化は使えましたが、こちらではCloudTrailに証跡を残すことができませんでした。
今回OAIからOACに変更しオリジンのS3バケットをKMSで暗号化してみました。その際にCloud Trailでどのような証跡が取れるようになるのか確認しました。OAIからOACへの移行方法も含めて紹介します。
OAIでオリジンへのアクセスをCloudFrontからに限定する構成を構築
まずはOAIで構築します。
検証のためSSE-S3で暗号化します
オリジンのS3バケットにホストしているのはHello World!!
と表示するだけのHTMLファイルです。
AWS環境の構築にはCDKを用いました。
import { Stack, StackProps, aws_s3, aws_cloudfront, aws_cloudfront_origins, aws_s3_deployment, aws_iam, RemovalPolicy, Duration, } from 'aws-cdk-lib'; import { Construct } from 'constructs'; export class S3HostingPracticeStack extends Stack { constructor( scope: Construct, id: string, props: StackProps, ) { super(scope, id, props); //オリジンに指定するS3バケット作成 const hostingBucket = new aws_s3.Bucket( this, `s3-hosting-bucket`, { bucketName: `s3-hosting-bucket-${Stack.of(this).account}`, removalPolicy: RemovalPolicy.DESTROY, encryption: aws_s3.BucketEncryption.S3_MANAGED, blockPublicAccess: aws_s3.BlockPublicAccess.BLOCK_ALL, // オリジンへのアクセスをCloudFrontに限定するためにBLOCK_ALL設定 }, ); //OAI作成 const oai = new aws_cloudfront.OriginAccessIdentity(this, 'OAI', { comment: 'OAI', }); //OAIにGetObjectを許可するバケットポリシーを作成しバケットに付与 const policyGetObjectForOAI = new aws_iam.PolicyStatement({ sid:"AllowOAIGetObject", effect: aws_iam.Effect.ALLOW, actions: ['s3:GetObject'], principals: [ new aws_iam.CanonicalUserPrincipal( oai.cloudFrontOriginAccessIdentityS3CanonicalUserId, ), ], resources: [`${hostingBucket.bucketArn}/*`],//s3:GetObjectはオブジェクトへの操作なので`/*`を付ける }); hostingBucket.addToResourcePolicy(policyGetObjectForOAI); //Distributionの作成 const websiteDistribution = new aws_cloudfront.Distribution( this, 'CloudFrontDistribution', { comment: 'website-distribution', defaultRootObject: 'index.html', defaultBehavior: { allowedMethods: aws_cloudfront.AllowedMethods.ALLOW_GET_HEAD, cachedMethods: aws_cloudfront.CachedMethods.CACHE_GET_HEAD, cachePolicy: aws_cloudfront.CachePolicy.CACHING_OPTIMIZED, viewerProtocolPolicy: aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, origin: new aws_cloudfront_origins.S3Origin(hostingBucket, { originAccessIdentity: oai, }), }, errorResponses: [ { ttl: Duration.seconds(300), httpStatus: 403, //forbidden responseHttpStatus: 403, responsePagePath: '/index.html', }, { ttl: Duration.seconds(300), httpStatus: 404, //not found responseHttpStatus: 404, responsePagePath: '/index.html', }, ], }, ); //S3バケットにデプロイするコンテンツの設定 new aws_s3_deployment.BucketDeployment(this, 'webSiteDeploy', { destinationBucket: hostingBucket, sources: [ aws_s3_deployment.Source.asset('./web-app'), ], distribution: websiteDistribution, distributionPaths: ['/*'], }); } }
コードの解説
何点かコードについて解説します。
- S3バケットのBlockPublicAccess
S3のオリジンへのアクセスをCloudFrontに限定するためにBlockPublicAccess全てブロックする設定としています。
blockPublicAccess: aws_s3.BlockPublicAccess.BLOCK_ALL,
-
OAIの権限を定めるバケットポリシー
与える権限のs3:GetObject
はオブジェクトへの操作なのでリソースの指定では末尾に/*
を付ける事を忘れないようにしてください。
AWS認定試験でもリソースの記述についての問で何回か出題されているのを見かけました。resources: [`${hostingBucket.bucketArn}/*`],
-
DistributionのDefault Root Objectの設定
こちらを設定しない状態でAWS Security HubのAWS 基礎セキュリティのベストプラクティス v1.0.0
を利用している場合、セキュリティチェックがFAILED
となります。 こちらはセキュリティチェック項目として重要度がCRITICAL
となります。 以下の弊社ブログでは設定しない場合の影響について解説していますので、是非参考にしていただければと思います。 -
CloudFrontのデフォルトルートオブジェクトを設定することで意図しないコンテンツの公開が防げるケースを実際に確認してみた
defaultRootObject: 'index.html',
動作確認
以下の通りバケットの暗号化はSSE-S3となっています。
CloudFrontのドメインにアクセスするとオリジンのバケットにホストしているHTMLが表示されました。
OAIのままバケットの暗号化をKMSにしてみる
OAIでは対応していないKMSに暗号化方法を変更した時にどうなるか確認してみました。
コードは以下の部分を追加修正しています。
//KMSキーの作成を追加 const kmsKey = new aws_kms.Key( this,"kms managed key", { description:"kms managed key", removalPolicy:RemovalPolicy.DESTROY } ) //S3バケットの作成部分の暗号化の指定を修正 const hostingBucket = new aws_s3.Bucket( this, `s3-hosting-bucket`, { bucketName: `s3-hosting-bucket-${Stack.of(this).account}`, removalPolicy: RemovalPolicy.DESTROY, encryptionKey:kmsKey, // 上記作成したKMSキーを指定 encryption: aws_s3.BucketEncryption.KMS, //バケットの暗号化をKMSに指定 blockPublicAccess: aws_s3.BlockPublicAccess.BLOCK_ALL, }, );
動作確認
CDKでのデプロイはエラーが発生する事無くできてしまいました。
以下の通りバケットの暗号化はKMSに切り替わっています。
この状態でCloudFrontのドメインにアクセスしてみると以下エラーが発生してHTMLファイルは表示されませんでした。
OAIからOACに変更
それではOACに変更してみます。
AWSのブログで変更方法について紹介されていたので参考にしました。
やることは結構簡単で以下3点となります。
- OACの作成
- ディストリビューションの
S3 バケットアクセス
をOACに変更 - OACがオリジンのオブジェクトをGetObjectできるようにバケットポリシーを変更
OACの作成
CloudFrontのトップページの左側のペインに有るオリジンアクセス
を開きます。
コントロール設定
からOAC作成できます。隣のアイデンティティ
のタブはOAIの作成用となります。既にレガシーと記載されていることから非推奨である事が窺えます
コントロール設定を作成
をクリックして作成を進めます。
OACの名前を記載します。
署名動作
は推奨の署名リクエスト (推奨)
を選択しオリジンタイプ
はS3
を選択します。
※ 署名動作
のリクエストに署名しない
を選択した場合、オリジンアクセス制御
を使用しない事になります。
作成をクリックしますと以下のような画面となりOACが作成されました。
ディストリビューションのS3 バケットアクセス
をOACに変更
ディストリビューションのオリジン
タブを開いて編集
をクリックしオリジンの編集画面に遷移します。
以下の設定を行います。
S3 バケットアクセス
をOrigin access control settings (recommended)
に変更
Origin access control
の下のプルダウンから先程作成したOACを選びます。
ポリシーをコピー
を押すとAWSが作ってくれたバケットポリシーがコピーされます。
S3 バケットアクセス許可に移動
を押すとバケットポリシーの編集画面が別タブで開きます。
こちらの編集画面は変更を保存
を押して完了してからバケットポリシーの変更に進んでください。
バケットポリシーの変更
変更前のOAI用のバケットポリシーが以下となっております。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowOAIGetObject", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E19UFSHZRALVTS" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::s3-hosting-bucket-************/*" } ] }
OAC
がオリジンにGetObjectできるようにするポリシーを入れ替えます。
OAC
に必要なポリシーについては以下のAWS公式ドキュメントにも記載があります。
以下が必要なポリシーとなります。
{ "Version": "2012-10-17", "Statement": { "Sid": "AllowOACGetObject", "Effect": "Allow", "Principal": { "Service": "cloudfront.amazonaws.com" // 変更点 }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::s3-hosting-bucket-************/*", "Condition": { // 変更点 "StringEquals": { "AWS:SourceArn": "arn:aws:cloudfront::<AWS アカウント ID>:distribution/<CloudFront distribution ID>" } } } }
ポリシーの変化点は以下です。
Principal
OAIのARN
からcloudfront.amazonaws.com
に変更
Condition
が追加されStringEquals
の条件にディストリビューションのARN
が指定されています。
ポリシーは先程オリジンの編集画面でコピーしたものがそのまま使えますので、以下のように入れ替えてください。
KMSのキーポリシーを変更
CloudFrontがKMSキーでオリジンのHTMLファイルを復号化できるようにする必要があるのでキーポリシーを追加します。
オリジンバケットのページのプロパティタブの中にあるデフォルトの暗号化
という項目から使用しているKMSキーのページを開けます。
このページでキーポリシーを追加します。
追加するポリシーは以下のとおりです
Conditionの指定にStringEquals条件としてディストリビューションのARNを指定します。
{ "Effect": "Allow", "Principal": { "Service": "cloudfront.amazonaws.com" }, "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey*" ], "Resource": "*", "Condition": { "StringEquals": { "aws:SourceArn": "arn:aws:cloudfront::<AWS アカウント ID>:distribution/<CloudFront distribution ID>"" } } }
動作確認
CloudFrontのドメインにアクセスしてHTMLコンテンツを表示
CloudFrontのドメインにアクセスするとHTMLファイルが表示されました!
CloudTrailの証跡ログの確認
オリジンを表示したタイミングで下の画像のような証跡ログが取れておりました。
userIdentityのinvokedByがcloudfrontとなっており、CloudFrontがKMSキーを使って復号化をしていることが確認できます。
さいごに
以下の弊社ブログではcloudFormationでOACを適応した環境の構築方法が紹介されております。
今後CDKでOACを使ったディストリビューションの構築方法がわかりましたら追って紹介したいと思います。